﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Xml;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Aga.Psad.Backend.Entities;
using Aga.Psad.Backend.Repositories;
using System.Web.Services.Protocols;
using Aga.Psad.Web.WDigDepFiel;

namespace Aga.Psad.Web.Helpers
{
    public class TicketAccesoHelper
    {
        // OJO! NO ES THREAD-SAFE 
        private static UInt32 _globalUniqueID = 0;

        private HttpServerUtility Server { get; set; }

        private TicketAccesoHelper() { }

        public TicketAccesoHelper(HttpServerUtility server)
        {
            this.Server = server;
        }

        private XmlDocument GetLoginTicketRequest()
        {
            XmlDocument XmlLoginTicketRequest;
            XmlNode xmlNodoUniqueId;
            XmlNode xmlNodoGenerationTime;
            XmlNode xmlNodoExpirationTime;
            XmlNode xmlNodoService;
            //string service = "wDigDepFiel";

            // PASO 1: Genero el Login Ticket Request 
            try
            {
                string xmlTemplatePath = Server.MapPath("~/xml/LoginTicketRequest.xml");
                XmlLoginTicketRequest = new XmlDocument();
                XmlLoginTicketRequest.Load(xmlTemplatePath);

                xmlNodoUniqueId = XmlLoginTicketRequest.SelectSingleNode("//uniqueId");
                xmlNodoGenerationTime = XmlLoginTicketRequest.SelectSingleNode("//generationTime");
                xmlNodoExpirationTime = XmlLoginTicketRequest.SelectSingleNode("//expirationTime");
                xmlNodoService = XmlLoginTicketRequest.SelectSingleNode("//service");

                // Las horas son UTC formato yyyy-MM-ddTHH:mm:ssZ
                xmlNodoGenerationTime.InnerText = DateTime.UtcNow.AddMinutes(-10).ToString("s") + "Z";
                xmlNodoExpirationTime.InnerText = DateTime.UtcNow.AddMinutes(+10).ToString("s") + "Z";
                xmlNodoUniqueId.InnerText = Convert.ToString(_globalUniqueID);
                //viene clavado en el template de xml
                //xmlNodoService.InnerText = service;

                _globalUniqueID += 1;

                return XmlLoginTicketRequest;
            }

            catch (Exception excepcionAlGenerarLoginTicketRequest)
            {
                throw new Exception("***Error generando el LoginTicketRequest: ObtenerLoginTicketResponse: " + excepcionAlGenerarLoginTicketRequest.Message);
            }

        }

        private string SignLoginTicketRequest(XmlDocument request)
        {
            try
            {
                string cmsFirmadoBase64;
                string certificatePath = Server.MapPath("~/Certificates/Certificate.pfx");
                X509Certificate2 certFirmante = X509Helper.ObtieneCertificadoDesdeArchivo(certificatePath);

                // Convierto el login ticket request a bytes, para firmar 
                Encoding EncodedMsg = Encoding.UTF8;
                byte[] msgBytes = EncodedMsg.GetBytes(request.OuterXml);

                // Firmo el msg y paso a Base64 
                byte[] encodedSignedCms = X509Helper.FirmaBytesMensaje(msgBytes, certFirmante);
                cmsFirmadoBase64 = Convert.ToBase64String(encodedSignedCms);

                return cmsFirmadoBase64;
            }

            catch (Exception excepcionAlFirmar)
            {
                throw new Exception("***Error firmando el LoginTicketRequest: ObtenerLoginTicketResponse: " + excepcionAlFirmar.Message);
            }
        }

        private string GetLoginTicketResponse(string cmsFirmado)
        {
            try
            {
                string loginTicketResponse;

                Wsaa.LoginCMSService servicioWsaa = new Wsaa.LoginCMSService();

                loginTicketResponse = servicioWsaa.loginCms(cmsFirmado);

                return loginTicketResponse;
            }
            catch (SoapException soapException)
            {
                // ToDo: Loguear Código y mensaje de error del web service.
                // Codigo: soapException.Code.Name
                // Mensaje: soapException.Message
                throw new Exception("***Error invocando al servicio WSAA: ObtenerLoginTicketResponse: " + soapException.Message);
            }
            catch (Exception excepcionAlInvocarWsaa)
            {
                throw new Exception("***Error invocando al servicio WSAA: ObtenerLoginTicketResponse: " + excepcionAlInvocarWsaa.Message);
            }
        }

        private TicketAcceso ExtractResponseData(string loginTicketResponse)
        {
            try
            {
                XmlDocument XmlLoginTicketResponse = new XmlDocument();
                XmlLoginTicketResponse.LoadXml(loginTicketResponse);

                TicketAcceso ticket = new TicketAcceso() { Service = "wDigDepFiel" };
                // No guardamos el ID que viene del servicio sino generamos uno en la base de datos así mantenemos la correlatividad.
                //ticket.Id = Int32.Parse(XmlLoginTicketResponse.SelectSingleNode("//uniqueId").InnerText);
                ticket.GenerationTime = DateTime.Parse(XmlLoginTicketResponse.SelectSingleNode("//generationTime").InnerText);
                ticket.ExpirationTime = DateTime.Parse(XmlLoginTicketResponse.SelectSingleNode("//expirationTime").InnerText);
                ticket.Sign = XmlLoginTicketResponse.SelectSingleNode("//sign").InnerText;
                ticket.Token = XmlLoginTicketResponse.SelectSingleNode("//token").InnerText;

                return ticket;
            }
            catch (Exception excepcionAlAnalizarLoginTicketResponse)
            {
                throw new Exception("Error analizando respuesta del WSAA: ObtenerLoginTicketResponse: " + excepcionAlAnalizarLoginTicketResponse.Message);
            }
        }

        internal TicketAcceso GetLoginTicket()
        {
            try
            {
                TicketsAccesoRepository repository = new TicketsAccesoRepository();

                TicketAcceso ticket = repository.GetLastTicket();

                if (ticket == null || ticket.ExpirationTime < DateTime.Now.AddMinutes(-5))
                {
                    ticket = RequestNewTicket();
                    repository.Save(ticket);
                }

                return ticket;
            }
            catch (Exception ex)
            {
                throw new Exception("Error obteniendo un ticket de acceso. Mensaje: " + ex.Message);
            }
        }

        private TicketAcceso RequestNewTicket()
        {

            try
            {
                var request = GetLoginTicketRequest();
                var signed = SignLoginTicketRequest(request);
                var response = GetLoginTicketResponse(signed);
                var ticket = ExtractResponseData(response);

                return ticket;

            }
            catch (Exception ex)
            {
                throw new Exception("Error solicitando nuevo ticket de acceso. Mensaje: " + ex.Message);
            }
        }

        internal Autenticacion GetAutentication()
        {
            TicketAcceso ticket = GetLoginTicket();

            return new Autenticacion
            {
                TipoAgente = "PSAD",
                Rol = "EXTE",
                Cuit = "30708670681",
                Token = ticket.Token,
                Sign = ticket.Sign
            };
        }
    }

}
